using System;
using System.Collections;
using System.Text;
using MCMLXVII.BackEnd.Common;

namespace MCMLXVII.Core.Common.BackEnd
{
	/// <summary>
	/// Backend Tracer
	/// </summary>
	public class Tracer
	{

		public event HostTraceEventHandler HostTrace;
		private static Tracer _Instance;

		private int _Cleaner=0;
		private Hashtable _HT;
		private TraceLevel _TraceLevel;
		private System.IO.StreamWriter _LogStream;
		private string _FileName;
		private bool _WindowsLog;
		
		static Tracer()
		{
            _Instance = new Tracer();
		}
        private Tracer()
        {
            _HT = new Hashtable();
            _Cleaner = 0;
            _TraceLevel = TraceLevel.Exceptions;            
        }
		public static Tracer GetInstance()
		{				
			return _Instance;
		}

		/// <summary>
		/// Fires the event that notifies the host service about news when the trace is enabled for this listener
		/// </summary>
		/// <param name="e">Host trace event arguments</param>
		protected virtual void OnHostTrace(HostTraceEventArgs e)
		{
			if (HostTrace != null) 
			{
				HostTrace(this, e);
            }
            else
            {
                TracerCache.AddTrace(e);
			}
		}

        public static HostTraceEventArgs[] GetTrace()
        {
            return TracerCache.GetTrace(true);
        }

		public static void StartHostTrace(string FileName, TraceLevel Level, bool WindowsLog)
		{            
			_Instance._TraceLevel = Level;
			_Instance._FileName=FileName;
			_Instance._LogStream=System.IO.File.CreateText(FileName);
			_Instance._LogStream.Write(ResourcesTools.RetornaLocPar(Resources.TraceStarted, DateTime.Now.ToString(), Level.ToString()));
			_Instance._WindowsLog=WindowsLog;
		}


		public static void StartHostTrace(TraceLevel Level, bool WindowsLog)
		{
			_Instance._TraceLevel = Level;
			_Instance._FileName="";
			_Instance._WindowsLog=WindowsLog;
			if (WindowsLog)
				EventLog.LogEvent(ResourcesTools.RetornaLocPar(Resources.TraceStarted, DateTime.Now.ToString(), Level.ToString()),System.Diagnostics.EventLogEntryType.Information) ;				
		}

		public static void StartActionTrace(string ActionName, Guid ActionGuid, TraceLevel Level)
		{
            lock (_Instance._HT.SyncRoot)
            {
                _Instance._HT.Add(ActionGuid, new ActionTraceInfo(ActionName, ActionGuid, Level));
            }
		}

		public static void StopHostTrace()
		{
			if (_Instance._FileName != "")
			{
				_Instance._LogStream.Flush();
				_Instance._LogStream.Close();
			}
			_Instance._TraceLevel = TraceLevel.None;
			_Instance._FileName="";
			_Instance._LogStream=null;
			_Instance._WindowsLog=false;
		}

		public static string StopActionTrace(Guid ActionGuid)
		{
            lock (_Instance._HT.SyncRoot)
            {
                if (_Instance._HT.ContainsKey(ActionGuid))
                {
                    string st = ((ActionTraceInfo)_Instance._HT[ActionGuid]).Content;
                    _Instance._HT.Remove(ActionGuid);
                    return st;
                }
                else
                    return ResourcesTools.RetornaLocPar(Resources.ActionTraceNotFound, ActionGuid.ToString());
            }
		}

		public static void Trace(Guid ActionGuid, TraceLevel Level, string Info)
		{
            lock (_Instance._HT.SyncRoot)
            {
                if (_Instance._HT.ContainsKey(ActionGuid) && (((ActionTraceInfo)_Instance._HT[ActionGuid]).Level >= Level))
                {
                    ((ActionTraceInfo)_Instance._HT[ActionGuid]).AddMessage(Info);
                }
            }
			if (Level <= _Instance._TraceLevel)
			{
				if (_Instance._WindowsLog)
				{
					System.Diagnostics.EventLogEntryType et = System.Diagnostics.EventLogEntryType.Information;
					switch(Level)
					{
						case TraceLevel.Info:
							et = System.Diagnostics.EventLogEntryType.Information;
							break;
						case TraceLevel.Exceptions:
							et = System.Diagnostics.EventLogEntryType.Error;
							break;
						case TraceLevel.Verbose:
							et = System.Diagnostics.EventLogEntryType.Information;
							break;
					}
					EventLog.LogEvent(ResourcesTools.RetornaLocPar(Resources.ActionTrace, ActionGuid.ToString(), Info), et);
				}
				// Completar Action Name !!!
				_Instance.OnHostTrace(new HostTraceEventArgs( "*", ActionGuid, ResourcesTools.RetornaLocPar(Resources.ActionTrace, ActionGuid.ToString(), Info)));
				
				if (_Instance._FileName != "")
				{
					_Instance._LogStream.WriteLine(ResourcesTools.RetornaLocPar(Resources.ActionTrace, ActionGuid.ToString(), Info));
				}
				_Instance._Cleaner++;
				if (_Instance._Cleaner > 1000) // More than 1000 action traces inits the clear process
					_Instance.ClearBuffer();
					if (_Instance._FileName != "")
						_Instance._LogStream.Flush();
			}
		}

		private void ClearBuffer()
		{
			lock(_Instance._HT.SyncRoot)
			{
				_Cleaner = 0;
				DateTime TestDate = System.DateTime.Now.AddSeconds(-600); // Clear actions that are more than 10 minutes old
				foreach(DictionaryEntry de in _HT)
				{
					if (((ActionTraceInfo)de.Value).LastUsed < TestDate)
						_HT.Remove(de.Key);
				}
			}
		}

		public static void Trace(string Info, TraceLevel Level)
		{
			if (Level <= _Instance._TraceLevel)
			{
				if (_Instance._WindowsLog)
				{
					System.Diagnostics.EventLogEntryType et= System.Diagnostics.EventLogEntryType.Warning;
					switch(Level)
					{
						case TraceLevel.Info:
							et = System.Diagnostics.EventLogEntryType.Information;
							break;
						case TraceLevel.Exceptions:
							et = System.Diagnostics.EventLogEntryType.Error;
							break;
						case TraceLevel.Verbose:
							et = System.Diagnostics.EventLogEntryType.Information;
							break;
					}
                    EventLog.LogEvent(Info, et);
                }
				
				_Instance.OnHostTrace(new HostTraceEventArgs( Info));
				
				if (_Instance._FileName != "")
				{
					_Instance._LogStream.WriteLine(Info);
				}
				_Instance._Cleaner++;
				if (_Instance._Cleaner > 50)
					_Instance.ClearBuffer();
			}
		}
	}
	internal class ActionTraceInfo
	{
		private Guid _ActionGuid;
		private string _ActionName;
		private TraceLevel _Level;
		private DateTime _LastUsed;
		private System.Text.StringBuilder _SBContents;

		internal ActionTraceInfo(string ActionName, Guid ActionGuid, TraceLevel Level)
		{
			_ActionGuid = ActionGuid;
			_ActionName = ActionName;
			_Level = Level;
			_SBContents = new System.Text.StringBuilder();
			_LastUsed = DateTime.Now;
		}

		public Guid ActionGuid
		{
			get{return _ActionGuid;}
		}

		public string ActionName
		{
			get {return _ActionName;}
		}
		public TraceLevel Level
		{
			get{return _Level;}
		}
		public void AddMessage(string st)
		{
			_LastUsed = DateTime.Now;
			_SBContents.Append(st);
		}
		public string Content
		{
			get {return _SBContents.ToString();}
		}
		public DateTime LastUsed
		{
			get {return _LastUsed;}
		}

	}
}
